#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"
using namespace MutexModule;
using namespace CondModule;

namespace BlockQueueMoudel
{
    static int gcap = 10;

    template <class T>
    class BlockQueue
    {
    private:
        bool IsFull() { return _q.size() == _cap; }
        bool IsEmpty() { return _q.empty(); }

    public:
        BlockQueue(int cap = gcap)
            : _cap(cap), _p_wait_num(0), _c_wait_num(0)
        {}

        ~BlockQueue() {}

        // 生产者
        void Push(const T &in)
        {
            LockGuard lockguard(_mutex);

            while (IsFull())
            {
                std::cout << "生产者进入等待..." << std::endl;

                _p_wait_num++;
                _producer_cond.Wait(_mutex);
                _p_wait_num--;

                std::cout << "生产者已被唤醒..." << std::endl;
            }
            _q.push(in);

            if (_c_wait_num)
            {
                // std::cout << "唤醒消费者" << std::endl;
                _consumer_cond.Signal();
            }
        }

        // 消费者
        void Pop(T *out)
        {
            LockGuard lockguard(_mutex);

            while (IsEmpty())
            {
                std::cout << "消费者进入等待..." << std::endl;

                _c_wait_num++;
                _consumer_cond.Wait(_mutex);
                _c_wait_num--;

                std::cout << "消费者已被唤醒..." << std::endl;
            }
            *out = _q.front();
            _q.pop();

            if (_p_wait_num)
            {
                // std::cout << "唤醒生产者" << std::endl;
                _producer_cond.Signal();
            }
        }

    private:
        std::queue<T> _q;    // 保存数据, 临界资源
        int _cap;            // bq的最大容量
        Mutex _mutex;        // 互斥锁
        Cond _producer_cond; // 生产者条件变量
        Cond _consumer_cond; // 消费者条件变量

        int _p_wait_num; // 生产者线程等待个数
        int _c_wait_num; // 消费者线程等待个数
    };
}